﻿using Journey.Data.EntityFramework;
using System.Collections.Generic;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;

namespace Journey
{
    using Autofac;
    using Autofac.Integration.Mvc;
    using Journey.Adapter;
    using Journey.Environment;
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Web;
    using System.Web.Mvc;

    /// <summary>
    /// 预启动程序。
    /// </summary>
    public class Starter
    {
        /// <summary>
        /// 预启动程序。
        /// </summary>
        private static Starter _starter;

        /// <summary>
        /// 锁。
        /// </summary>
        private readonly static object LockObject = new object();

        /// <summary>
        /// 初始化的外部委托。
        /// </summary>
        private readonly Action<ContainerBuilder> _initialization;

        /// <summary>
        /// 初始化预启动程序。
        /// </summary>
        /// <param name="initialization">委托方法。</param>
        public Starter(Action<ContainerBuilder> initialization)
        {
            this._initialization = initialization;
        }

        /// <summary>
        /// 创建预启动程序。
        /// </summary>
        /// <returns>预启动程序。</returns>
        public static Starter CreateStarter()
        {
            return CreateStarter(builder => { });
        }

        /// <summary>
        /// 创建预启动程序。
        /// </summary>
        /// <param name="initialization">委托方法。</param>
        /// <returns>预启动程序。</returns>
        public static Starter CreateStarter(Action<ContainerBuilder> initialization)
        {
            if (_starter != null) return _starter;

            lock (LockObject)
            {
                _starter = new Starter(initialization);
            }

            return _starter;
        }

        /// <summary>
        /// 应用程序启动。
        /// </summary>
        /// <param name="application">应用程序启动。</param>
        public void OnApplicationStart(HttpApplication application)
        {
            var builder = new ContainerBuilder();

            //注册程序集的集合中所有类型到它所实现的接口。
            builder.RegisterAssemblyTypes(GetCurrentAssemblies()).AsImplementedInterfaces();

            //注册程序集的集合中的所有控制器。
            builder.RegisterControllers(GetCurrentAssemblies());

            //注册外部方法。
            this._initialization(builder);

            //生成 IoC 容器。
            var container = builder.Build();

            //实现对控制器进行注入。
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

            //注册并封装对象管理容器。
            IoC.RegisterContainer(new IoCAutofacContainer(container));

            //注册对象映射器。
            TypeAdapterFactory.SetCurrent(new AutomapperTypeAdapterFactory());

            //初始化 EntityFramework。
            InitEntityFramework();
        }

        /// <summary>
        /// 获得当前程序域的自定义类库的程序集的集合。
        /// </summary>
        /// <returns>程序集的数组集合。</returns>
        private static Assembly[] GetCurrentAssemblies()
        {
            return AppDomain.CurrentDomain.GetAssemblies().Where(
                assembly =>
                    !assembly.FullName.ToLower().StartsWith("mscorlib") &&
                    !assembly.FullName.ToLower().StartsWith("Antlr3".ToLower()) &&
                    !assembly.FullName.ToLower().StartsWith("App_global.asax".ToLower()) &&
                    !assembly.FullName.ToLower().StartsWith("SMDiagnostics".ToLower()) &&
                    !assembly.FullName.ToLower().StartsWith("WebGrease".ToLower()) &&
                    !assembly.FullName.ToLower().StartsWith("Newtonsoft.Json".ToLower()) &&
                    !assembly.FullName.ToLower().StartsWith("system") &&
                    !assembly.FullName.ToLower().StartsWith("microsoft") &&
                    !assembly.FullName.ToLower().StartsWith("autofac") &&
                    !assembly.FullName.ToLower().StartsWith("automapper") &&
                    !assembly.FullName.ToLower().StartsWith("entityframework")).ToArray();
        }

        /// <summary>
        /// 初始化并预热 EntityFramework。
        /// </summary>
        private static void InitEntityFramework()
        {
            using (var dbcontext = new MainObjectContext())
            {
                var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext;
                var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
                mappingCollection.GenerateViews(new List<EdmSchemaError>());
            }
        }
    }
}